package com.ideaction.service;

import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.ideaction.entity.player.Player;
import com.ideaction.entity.player.ResourceData;
import com.ideaction.game.utils.Localization;
import com.ideaction.game.utils.SimpleDBUtil;
import com.ideaction.game.utils.TimeKit;
import com.ideaction.globalconst.NoticeKeyConst;
import com.ideaction.globalconst.ParamKey;
import com.ideaction.globalconst.Result;
import com.ideaction.req.PlayerCommonReqBody;
import com.ideaction.utils.TimeUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.ssl.TrustStrategy;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestBody;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;
import javax.net.ssl.SSLContext;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Base64;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import static java.nio.charset.StandardCharsets.UTF_8;

@Service
@Slf4j
public class AntiAddictionServiceImp {
    
    @Autowired
    private MongoTemplate playerMongoTemplate;
    
    public static final int HTTP_RESPONSE_STATUS_CODE_OK = 200, SUCCEED = 0;
    
    public static String CHECK_URL = "https://api.wlc.nppa.gov.cn/idcard/authentication/check";
    
    public static String LOGIN_OUT_URL = "http://api2.wlc.nppa.gov.cn/behavior/collection/loginout";
    
    public static String QUERY_ID_CARD = "http://api2.wlc.nppa.gov.cn/idcard/authentication/query";
    
    public static int LOGIN = 1, LOGOUT = 0;
    
    /**
     * 实名认证
     *
     * @return
     */
    public String idCardCheck(@RequestBody PlayerCommonReqBody playerCommonReqBody) {
        String userID = playerCommonReqBody.getUserId();
        String name = playerCommonReqBody.getParam("Name");
        String idNum = playerCommonReqBody.getParam("IdNum");
        String language = playerCommonReqBody.getParam("Language");
        
        Result r = Result.build();
        
        Map<String, Object> paramaters = Maps.newHashMap();
        paramaters.put("ai", userID);
        paramaters.put("name", name);
        paramaters.put("idNum", idNum);
        
        JSONObject jsonObject = new JSONObject(paramaters);
        byte[] keyBytes = hexStringToByte(SECRET_KEY);
        
        String encryptStr = "{\"data\":\"" + aesGcmEncrypt(jsonObject.toString(), keyBytes) + "\"}";
        long timestamps = TimeKit.getMillisTime();
        String sign = "";
        try {
            sign = encryption(encryptStr, timestamps);
        } catch (NoSuchAlgorithmException e) {
            log.warn("sign() err", e);
            return r.error(NoticeKeyConst.SYSTEM_ERR_ENCRYPTION_DEFEATED, language);
        }
        
        jsonObject = JSONObject.parseObject(doPost(CHECK_URL, sign, encryptStr, timestamps));
        
        if (jsonObject.getInteger("errcode") == 0) {
            if (SimpleDBUtil.existsPlayer(playerMongoTemplate, userID)) {
                Player player = SimpleDBUtil.findDataByUserId(playerMongoTemplate, userID, Player.class);
                player.setRealName(name);
                player.setRealID(idNum);
                playerMongoTemplate.save(player);
            } else {
                return r.error(NoticeKeyConst.USER_NOT_EXIST, language);
            }
        } else {
            String errorInfo = jsonObject.getString("errmsg");
            return r.error(errorInfo, language);
        }
        
        return r.response();
    }
    
    /**
     * 未成年人检查
     *
     * @return
     */
    public String adultCheck(PlayerCommonReqBody playerCommonReqBody) {
        String userID = playerCommonReqBody.getUserId();
        String realID = playerCommonReqBody.getParam("RealID");
        String language = playerCommonReqBody.getParam("Language");
        
        Result r = Result.build();
        
        if (realID == null || "".equals(realID)) {
            return r.error(NoticeKeyConst.UNFINISHED_ID_CARD_CHECK, language);
        }
        
        String birthday = realID.substring(6, realID.length() - 4);
        int age = TimeUtil.getAgeByBirth(birthday);
        if (age < 18) {
            return r.error(NoticeKeyConst.YOUR_ACCOUNT_IS_UNDER_AGE, language);
        }
        return r.response();
    }
    
    /**
     * (二)	实名认证结果查询
     *
     * @return
     */
    public String queryIdCard(String userName) throws NoSuchAlgorithmException {
        Map<String, Object> paramaters = Maps.newHashMap();
        paramaters.put("ai", userName);
        
        JSONObject jsonObject = new JSONObject(paramaters);
        byte[] keyBytes = hexStringToByte(SECRET_KEY);
        
        String encryptStr = "{\"data\":\"" + aesGcmEncrypt(jsonObject.toString(), keyBytes) + "\"}";
        long timestamps = TimeKit.getMillisTime();
        String sign = encryption(encryptStr, timestamps);
        
        return doGet(QUERY_ID_CARD, paramaters, sign, timestamps);
    }
    
    /**
     * @param player
     * @param bt     游戏用户行为类型 0：下线       1：上线
     * @return
     * @throws NoSuchAlgorithmException
     * @throws JSONException
     */
    public String loginOut(Player player, int bt) throws NoSuchAlgorithmException, JSONException {
        Map<String, Object> paramaters = Maps.newHashMap();
        List<JSONObject> collections = Lists.newArrayList();
        collections.add(new JSONObject());
        collections.get(0).put("no", 1);
        collections.get(0).put("si", player.getUserName());
        collections.get(0).put("bt", bt);
        collections.get(0).put("ot", TimeKit.getSecondTime());
        collections.get(0).put("ct", player.getRealID() != null && !"".equals(player.getRealID()) ? 0 : 2);
        
        paramaters.put("collections", collections);
        
        JSONObject jsonObject = new JSONObject(paramaters);
        
        byte[] keyBytes = hexStringToByte(SECRET_KEY);
        
        String encryptStr = aesGcmEncrypt(jsonObject.toString(), keyBytes);
        long timestamps = TimeKit.getMillisTime();
        String sign = encryption(encryptStr, timestamps);
        return doPost(LOGIN_OUT_URL, sign, encryptStr, timestamps);
    }
    
    public String doPost(String url, String sign, String body, long timestamps) {
        HttpPost httpReq = new HttpPost(url);
        
        // Create an HttpClient instance without SSL authentication.
        CloseableHttpClient httpclient = null;
        try {
            httpclient = getIgnoeSSLClient();
        } catch (Exception e1) {
            log.warn("doPost", e1);
            e1.printStackTrace();
        }
        try {
            //            if (paramaters != null) {
            //                List<NameValuePair> paramPairs = new ArrayList<NameValuePair>();
            //                BasicNameValuePair bnv;
            //                for (Map.Entry<String, Object> entry : paramaters.entrySet()) {
            //                    bnv = new BasicNameValuePair(entry.getKey(), entry.getValue().toString());
            //                    paramPairs.add(bnv);
            //                }
            //                httpReq.setEntity(new UrlEncodedFormEntity(paramPairs, "UTF-8"));
            //            }
            httpReq.addHeader("appId", APP_ID);
            httpReq.addHeader("bizId", BIZ_ID);
            httpReq.addHeader("timestamps", "" + timestamps);
            httpReq.addHeader("sign", sign);
            httpReq.setHeader("Content-type", "application/json");
            httpReq.setEntity(new StringEntity(body));
            
            String responseParamPairs = "";
            HttpResponse resp = httpclient.execute(httpReq);
            if (null != resp && HTTP_RESPONSE_STATUS_CODE_OK == resp.getStatusLine().getStatusCode()) {
                responseParamPairs = EntityUtils.toString(resp.getEntity());
            }
            return responseParamPairs;
        } catch (Exception e) {
            log.warn("doPost", e);
            e.printStackTrace();
            return null;
        } finally {
            try {
                httpclient.close();
            } catch (IOException e) {
                log.warn("doPost", e);
                e.printStackTrace();
            }
        }
    }
    
    /**
     * 向指定URL发送GET方法的请求
     *
     * @param url   发送请求的URL
     * @param param 请求参数，请求参数应该是 name1=value1&name2=value2 的形式。
     * @return URL 所代表远程资源的响应结果
     */
    public String doGet(String url, Map param, String sign, long timestamps) {
        String result = "";
        BufferedReader in = null;
        try {
            String urlNameString = url;
            
            if (param != null && param.size() > 0) {
                urlNameString += "?";
                for (Object key : param.keySet()) {
                    urlNameString += key + "=" + param.get(key) + "&";
                }
                urlNameString = urlNameString.substring(0, urlNameString.length() - 1);
            }
            
            URL realUrl = new URL(urlNameString);
            // 打开和URL之间的连接
            URLConnection connection = realUrl.openConnection();
            // 设置通用的请求属性
            connection.setRequestProperty("accept", "*/*");
            connection.setRequestProperty("connection", "Keep-Alive");
            connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
            connection.setRequestProperty("appId", APP_ID);
            connection.setRequestProperty("bizId", BIZ_ID);
            connection.setRequestProperty("timestamps", "" + timestamps);
            connection.setRequestProperty("sign", sign);
            // 建立实际的连接
            connection.connect();
            // 获取所有响应头字段
            Map<String, List<String>> map = connection.getHeaderFields();
            // 遍历所有的响应头字段
            for (String key : map.keySet()) {
                log.info(key + "--->" + map.get(key));
            }
            // 定义 BufferedReader输入流来读取URL的响应
            in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
            String line;
            while ((line = in.readLine()) != null) {
                result += line;
            }
        } catch (Exception e) {
            log.info("发送GET请求出现异常！" + e);
            e.printStackTrace();
        }
        // 使用finally块来关闭输入流
        finally {
            try {
                if (in != null) {
                    in.close();
                }
            } catch (Exception e2) {
                e2.printStackTrace();
            }
        }
        return result;
    }
    
    private CloseableHttpClient getIgnoeSSLClient() throws Exception {
        SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(null, new TrustStrategy() {
            @Override
            public boolean isTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
                return true;
            }
        }).build();
        // Create an HttpClient.
        CloseableHttpClient client = HttpClients.custom().setSSLContext(sslContext).
                setSSLHostnameVerifier(new NoopHostnameVerifier()).build();
        return client;
    }
    
    public static String SECRET_KEY = "f9ce9e5b6180852e5dda2258c095f768", APP_ID = "aed6fb4349ec41c0bd7d4fb99cbda3f4", BIZ_ID = "1199000772";
    
    public static void main(String[] args) throws Exception {
        //        try {
        //            String data = "{\"ai\":\"100000000000000001\",\n" +
        //                "\"name\":\"刘俊池\",\n" +
        //                "\"idNum\":\"510125199812163811\"}";
        //            System.err.println(encryption(data));
        //
        //        }catch (Exception e){
        //            System.err.println(e);
        //        }
        //        log.info(idCardCheck("lonshhao","刘俊池", "510125199812163811"));
        String idCard = "510125199812163831";
        System.err.println(idCard.substring(idCard.length() - 1, idCard.length()));
    }
    
    /**
     * 加密方法
     *
     * @return
     * @throws NoSuchAlgorithmException
     */
    public String encryption(String encryptStr, long timestamps) throws NoSuchAlgorithmException {
        // 1.3 处理密钥（将16进制字符串密钥转换为byte数组）
        
        // 2 业务参数加密（AES-128/GCM + BASE64算法加密）计算
        log.info("业务参数加密结果：" + encryptStr);
        // 解密
        //        String decryptStr = aesGcmDecrypt(encryptStr, keyBytes);
        //        System.out.println("业务参数解密结果：" + decryptStr);
        
        // 3 签名
        /*
         * 签名规则：1 将除去sign的系统参数和除去请求体外的业务参数，根据参数的key进行字典排序，并按照Key-Value的格式拼接成一个字符串。将请求体中的参数拼接在字符串最后。
         *          2 将secretKey拼接在步骤1获得字符串最前面，得到待加密字符串。
         *          3 使用SHA256算法对待加密字符串进行计算，得到数据签名。
         *          4 将得到的数据签名赋值给系统参数sign。
         */
        // 3.1 拼接待签名字符串（下方示例代码中相应字符串均为写死，仅用于参考拼接流程，具体请参照实际接口参数）
        StringBuilder sb = new StringBuilder();
        // 3.1.1 拼接密钥
        sb.append(SECRET_KEY);
        // 3.1.2 拼接除去sign的系统参数和除去请求体外的业务参数（含请求URL中的参数，例如get请求。注意需要字典排序）
        sb.append("appId" + APP_ID);
        sb.append("bizId" + BIZ_ID);
        sb.append("timestamps" + timestamps);
        // 3.1.3 拼接请求体（保持json字符串格式，data对应的值应为上方加密算法计算出的encryptStr加密字符串）
        sb.append(encryptStr);
        
        String toBeSignStr = sb.toString();
        log.info("待签名字符串：" + toBeSignStr);
        
        // 3.1 签名计算（SHA256）
        String sign = sign(toBeSignStr);
        log.info("签名结果：" + sign);
        
        return sign;
    }
    
    /**
     * <p>@title sign</p>
     * <p>@description 签名</p>
     *
     * @param toBeSignStr 待签名字符串
     * @return java.lang.String
     */
    private String sign(String toBeSignStr) throws NoSuchAlgorithmException {
        MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
        messageDigest.update(toBeSignStr.getBytes(UTF_8));
        return byteToHexString(messageDigest.digest());
    }
    
    /**
     * <p>@title byteToHexString</p>
     * <p>@description byte数组转化为16进制字符串</p>
     *
     * @param bytes byte数组
     * @return java.lang.String
     */
    private String byteToHexString(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte aByte : bytes) {
            String strHex = Integer.toHexString(aByte);
            if (strHex.length() > 3) {
                sb.append(strHex.substring(6));
            } else {
                if (strHex.length() < 2) {
                    sb.append("0").append(strHex);
                } else {
                    sb.append(strHex);
                }
            }
        }
        return sb.toString();
    }
    
    /**
     * <p>@title aesGcmEncrypt</p>
     * <p>@description Aes-Gcm加密</p>
     *
     * @param content 待加密文本
     * @param key     密钥
     * @return java.lang.String
     */
    private String aesGcmEncrypt(String content, byte[] key) {
        try {
            // 根据指定算法ALGORITHM自成密码器
            Cipher cipher = Cipher.getInstance("AES/GCM/PKCS5Padding");
            SecretKeySpec skey = new SecretKeySpec(key, "AES");
            cipher.init(Cipher.ENCRYPT_MODE, skey);
            //获取向量
            byte[] ivb = cipher.getIV();
            byte[] encodedByteArray = cipher.doFinal(content.getBytes(UTF_8));
            byte[] message = new byte[ivb.length + encodedByteArray.length];
            System.arraycopy(ivb, 0, message, 0, ivb.length);
            System.arraycopy(encodedByteArray, 0, message, ivb.length, encodedByteArray.length);
            return Base64.getEncoder().encodeToString(message);
        } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException e) {
            //建议自行调整为日志输出或抛出异常
            return null;
        }
    }
    
    /**
     * <p>@title hexStringToByte</p>
     * <p>@description 十六进制string转二进制byte[]</p>
     *
     * @param str 十六进制字符串
     * @return byte[]
     */
    private byte[] hexStringToByte(String str) {
        byte[] baKeyword = new byte[str.length() / 2];
        for (int i = 0; i < baKeyword.length; i++) {
            try {
                baKeyword[i] = (byte) (0xff & Integer.parseInt(str.substring(i * 2, i * 2 + 2), 16));
            } catch (Exception e) {
                //建议自行调整为日志输出或抛出异常
                log.warn("game.logicServer.idCard.ChinaIDCardManager.hexStringToByte err", e);
            }
        }
        return baKeyword;
    }
}
